#==================================================
#	Turn Controller
#==================================================
# @datetime: 2021-9-12 10:58:45
#==================================================

##  转动
class_name TurnController
extends Node2D


##  [code]radian[/code] 为旋转的弧度。连接这个信号调用 [Node2D] 节点的 [method rotate] 方法进行旋转
signal rotated(radian: float)
##  转向完成
signal turn_finished


##  执行方式
enum ExecuteMode {
	# 需要不断地调用旋转方法进行旋转
	MANUAL,
	# 以 _physics_process 线程进行调用旋转到的节点，只用调用一次旋转方法则会自动旋转到目标位置
	PHYSICS,
	# 以 _process 线程进行调用旋转到的节点，只用调用一次旋转方法则会自动旋转到目标位置
	PROCESS,
}

@export
var execute_mode : ExecuteMode = ExecuteMode.MANUAL:
	set(v):
		execute_mode = v
		__update_process()
## 根据这个节点的方向进行旋转
@export
var rotate_by_node : Node2D
## 旋转速度
@export 
var rotation_speed := 3.0
## 偏移的角度（这会决定贴图方向）（默认顶部朝向目标位置）
@export_range(0, 360.0) 
var offset_degress := 0.0 : set=set_offset_degress


# 偏移的弧度
@onready 
var __offset_rot := deg_to_rad(offset_degress)

var __to : Vector2 = Vector2(0,0)
var __dot : float = 0.0


#==================================================
#   Set/Get
#==================================================
func set_offset_degress(value: float) -> void:
	offset_degress = value
	__offset_rot = deg_to_rad(offset_degress)

func get_last_dot() -> float:
	return __dot


#============================================================
#  内置
#============================================================
func _ready() -> void:
	self.execute_mode = execute_mode
	if rotate_by_node:
		__to = rotate_by_node.global_position


func _physics_process(delta: float) -> void:
	central_to(__to, delta)


func _process(delta: float) -> void:
	central_to(__to, delta)


#==================================================
#   自定义方法
#==================================================
# 更新线程
func __update_process():
	set_process(false)
	set_physics_process(false)
	if execute_mode == ExecuteMode.PROCESS:
		set_process(true)
	elif execute_mode == ExecuteMode.PHYSICS:
		set_physics_process(true)


##  转向到目标位置。这个是手动控制，调用后不会自动旋转到目标方向。
##[br]
##[br][code]from[/code]  角色位置
##[br][code]to[/code]  目标位置
func to(from: Vector2, to: Vector2, delta: float) -> void:
	to_dir(from.direction_to(to), delta)


##  中心旋转。以当前 rotate_by_node 位置旋转到目标位置
func central_to(to: Vector2, delta: float):
	if rotate_by_node:
		__to = to
		to(rotate_by_node.global_position, to, delta)
		__update_process()

##  停止旋转
func stop():
	set_physics_process(false)
	set_process(false)

##  转向到目标方向
##[br]
##[br][code]dir[/code]  目标方向
func to_dir(dir: Vector2, delta: float):
	# 弧度方向
	#（站立的时候，人物头部是朝向上面的，所以为 Vector2.UP）
	# 但这样造成旋转的值多了 PI/2，所以减去，加上用户设置的偏移的弧度
	var rot_dir = Vector2.UP.rotated(rotate_by_node.global_rotation - PI/2 + __offset_rot)
	# [面向角度]与[自身当前旋转角度]的点积
	__dot = dir.dot(rot_dir)
	
	# 旋转速度
	var r_speed = rotation_speed * delta
	# 旋转的值
	var radian = sign(__dot) * r_speed
	
	if (
		# 点积大小超过 rotation_speed 则进行旋转
		# 防止速度过快时的抖动问题
		abs(__dot) > r_speed
		
		# 自身面向角度 和 自身到目标点的角度 不是在自身的背面
		# 有时在背面的 dot 值也会为 0，防止在这个时候突然转向到后面
		# 下面这个 rot_dir 旋转了 -PI/2 （90度）是因为角度默认朝上方
		# 所以逆时针旋转 90度 以跟默认角度朝左方的角度相匹配
		|| abs(rot_dir.rotated(-PI/2).angle() - dir.angle()) > PI/2
	):
		rotated.emit(radian)
	
	else:
		set_process(false)
		set_physics_process(false)
		
		rotate_by_node.global_rotation = dir.angle() - PI/2 - __offset_rot
		turn_finished.emit()

